/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.desktop.partition;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.gephi.partition.api.Partition;
import org.gephi.partition.api.PartitionController;
import org.gephi.partition.api.PartitionModel;
import org.gephi.partition.spi.Transformer;
import org.gephi.partition.spi.TransformerUI;
import org.gephi.ui.utils.BusyUtils;
import org.gephi.ui.utils.BusyUtils.BusyLabel;
import org.jdesktop.swingx.JXBusyLabel;
import org.gephi.ui.utils.UIUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;

/**
 *
 * @author Mathieu Bastian
 */
public class PartitionChooser extends javax.swing.JPanel implements PropertyChangeListener {

    //Const
    private final String NO_SELECTION;
    private final String BUSY_MSG;
    private final String GROUP_LABEL;
    private final String UNGROUP_LABEL;
    private final String SHOW_PIE;
    private final String HIDE_PIE;
    //Architecture
    private PartitionModel model;

    public PartitionChooser() {
        initComponents();
        initEvents();
        if (UIUtils.isAquaLookAndFeel()) {
            setBackground(UIManager.getColor("NbExplorerView.background"));
        }
        NO_SELECTION = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.choose.text");
        BUSY_MSG = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.busyMessage");
        GROUP_LABEL = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.group.label");
        UNGROUP_LABEL = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.ungroup.label");
        SHOW_PIE = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.showpie.label");
        HIDE_PIE = NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.hidepie.label");
        refreshModel();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        chooserPanel = new javax.swing.JPanel();
        partitionComboBox = new javax.swing.JComboBox();
        refreshToolbar = new javax.swing.JToolBar();
        refreshBusyLabel = new JXBusyLabel(new Dimension(18,18));
        refreshButton = new javax.swing.JButton();
        controlPanel = new javax.swing.JPanel();
        applyButton = new javax.swing.JButton();
        groupLink = new org.jdesktop.swingx.JXHyperlink();
        pieLink = new org.jdesktop.swingx.JXHyperlink();
        centerScrollPane = new javax.swing.JScrollPane();

        setLayout(new java.awt.BorderLayout());

        chooserPanel.setOpaque(false);
        chooserPanel.setLayout(new java.awt.GridBagLayout());

        partitionComboBox.setPreferredSize(new java.awt.Dimension(56, 25));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        chooserPanel.add(partitionComboBox, gridBagConstraints);

        refreshToolbar.setFloatable(false);
        refreshToolbar.setRollover(true);
        refreshToolbar.setOpaque(false);

        refreshBusyLabel.setText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.refreshBusyLabel.text")); // NOI18N
        refreshBusyLabel.setVisible(false);
        refreshToolbar.add(refreshBusyLabel);

        refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/partition/resources/refresh.png"))); // NOI18N
        refreshButton.setText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.refreshButton.text")); // NOI18N
        refreshButton.setToolTipText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.refreshButton.toolTipText")); // NOI18N
        refreshButton.setFocusable(false);
        refreshButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        refreshButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        refreshToolbar.add(refreshButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 0, 0);
        chooserPanel.add(refreshToolbar, gridBagConstraints);

        add(chooserPanel, java.awt.BorderLayout.PAGE_START);

        controlPanel.setOpaque(false);
        controlPanel.setLayout(new java.awt.GridBagLayout());

        applyButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/partition/resources/apply.gif"))); // NOI18N
        applyButton.setText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.applyButton.text")); // NOI18N
        applyButton.setMargin(new java.awt.Insets(0, 14, 0, 14));
        applyButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                applyButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 3, 5);
        controlPanel.add(applyButton, gridBagConstraints);

        groupLink.setClickedColor(new java.awt.Color(0, 51, 255));
        groupLink.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/partition/resources/cluster.png"))); // NOI18N
        groupLink.setText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.groupLink.text")); // NOI18N
        groupLink.setToolTipText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.groupLink.toolTipText")); // NOI18N
        groupLink.setEnabled(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 1, 0);
        controlPanel.add(groupLink, gridBagConstraints);

        pieLink.setClickedColor(new java.awt.Color(0, 51, 255));
        pieLink.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/partition/resources/pie.png"))); // NOI18N
        pieLink.setText(org.openide.util.NbBundle.getMessage(PartitionChooser.class, "PartitionChooser.pieLink.text")); // NOI18N
        pieLink.setEnabled(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new java.awt.Insets(0, 10, 1, 0);
        controlPanel.add(pieLink, gridBagConstraints);

        add(controlPanel, java.awt.BorderLayout.PAGE_END);

        centerScrollPane.setBorder(null);
        centerScrollPane.setOpaque(false);
        add(centerScrollPane, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void applyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyButtonActionPerformed
        PartitionController partitionController = Lookup.getDefault().lookup(PartitionController.class);
        partitionController.transform(model.getSelectedPartition(), model.getSelectedTransformer());
    }//GEN-LAST:event_applyButtonActionPerformed

    private void initEvents() {
        partitionComboBox.addItemListener(new ItemListener() {

            public void itemStateChanged(ItemEvent e) {
                if (partitionComboBox.getSelectedItem() == NO_SELECTION && model.getSelectedPartition() == null) {
                    return;
                }
                if (partitionComboBox.getSelectedItem() == model.getSelectedPartition()) {
                    return;
                }
                if (model.isWaiting()) {
                    return;
                }
                PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                pc.setSelectedPartition(partitionComboBox.getSelectedItem() == NO_SELECTION ? null : (Partition) partitionComboBox.getSelectedItem());
            }
        });
        groupLink.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (groupLink.getText().equals(GROUP_LABEL)) {
                    PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                    pc.group(model.getSelectedPartition());
                    refreshGrouped();
                } else {
                    PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                    pc.ungroup(model.getSelectedPartition());
                    refreshGrouped();
                }
            }
        });

        pieLink.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                pc.showPie(pieLink.getText().equals(SHOW_PIE) ? true : false);
            }
        });
        refreshButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                refreshBusyLabel.setVisible(true);
                refreshButton.setVisible(false);
                ((JXBusyLabel) refreshBusyLabel).setBusy(true);
                new Thread(new Runnable() {

                    public void run() {
                        PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                        pc.refreshPartitions();
                        SwingUtilities.invokeLater(new Runnable() {

                            public void run() {
                                ((JXBusyLabel) refreshBusyLabel).setBusy(false);
                                refreshButton.setVisible(true);
                                refreshBusyLabel.setVisible(false);
                                refreshPartitions();
                            }
                        });
                    }
                }).start();
            }
        });
    }
    private TransformerUI currentUI;
    private JPanel currentPanel;

    private void refreshTransformerBuilder() {
        if (currentUI != null) {
            currentUI.unsetup();
            currentUI = null;
            currentPanel = null;
        }
        if (model.getSelectedTransformerBuilder() == null) {
            centerScrollPane.setViewportView(null);
        } else {
            Transformer t = model.getSelectedTransformer();
            currentUI = model.getSelectedTransformerBuilder().getUI();
            if (model.getSelectedPartition() != null) {
                currentPanel = currentUI.getPanel();
                currentPanel.setOpaque(false);
                currentUI.setup(model.getSelectedPartition(), t);
            }
        }
    }

    private void refreshPartitions() {
        partitionComboBox.setModel(getComboBoxModel());
    }

    private DefaultComboBoxModel getComboBoxModel() {
        DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
        Partition[] partitionArray = new Partition[0];
        if (model.getSelectedPartitioning() == PartitionModel.NODE_PARTITIONING) {
            partitionArray = model.getNodePartitions();
        } else if (model.getSelectedPartitioning() == PartitionModel.EDGE_PARTITIONING) {
            partitionArray = model.getEdgePartitions();
        }
        if (partitionArray.length > 0) {
            comboBoxModel.addElement(NO_SELECTION);
            for (Partition p : partitionArray) {
                comboBoxModel.addElement(p);
                if (p == model.getSelectedPartition()) {
                    comboBoxModel.setSelectedItem(p);
                }
            }
        }
        return comboBoxModel;
    }
    private BusyLabel busyLabel;

    private void refreshWaiting() {
        if (model.isWaiting()) {
            busyLabel = BusyUtils.createCenteredBusyLabel(centerScrollPane, BUSY_MSG, null);
            busyLabel.setBusy(true);
        } else if (currentUI != null && currentPanel != null) {
            busyLabel.setBusy(false, currentPanel);
        } else if (busyLabel != null) {
            busyLabel.setBusy(false, null);
        }
    }

    private void refreshEnable() {
        if (model == null) {
            setEnable(false);
            if (currentUI != null) {
                centerScrollPane.setViewportView(null);
                currentUI.unsetup();
                currentUI = null;
                currentPanel = null;
            }
            return;
        }
        setEnable(true);
    }

    private void refreshGrouped() {
        if (model.getSelectedPartitioning() == PartitionModel.NODE_PARTITIONING) {
            groupLink.setVisible(true);
            if (model.getSelectedPartition() != null) {
                PartitionController pc = Lookup.getDefault().lookup(PartitionController.class);
                if (pc.isGroupable(model.getSelectedPartition())) {
                    groupLink.setText(GROUP_LABEL);
                    groupLink.setEnabled(true);
                    return;
                } else if (pc.isUngroupable(model.getSelectedPartition())) {
                    groupLink.setText(UNGROUP_LABEL);
                    groupLink.setEnabled(true);
                    return;
                }
            }
            groupLink.setEnabled(false);
        } else {
            groupLink.setVisible(false);
        }
    }
    private PartitionPie partitionPie;

    private void refreshPie() {
        if (model.getSelectedPartition() != null) {
            pieLink.setEnabled(true);
            if (model.isPie()) {
                pieLink.setText(HIDE_PIE);
                partitionPie = new PartitionPie();
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        partitionPie.setup(model.getSelectedPartition());
                        remove(centerScrollPane);
                        add(partitionPie, BorderLayout.CENTER);
                        revalidate();
                        repaint();
                    }
                });

                return;
            } else {
                pieLink.setText(SHOW_PIE);
            }

        } else {
            pieLink.setText(SHOW_PIE);
            pieLink.setEnabled(false);
        }
        if (!isAncestorOf(centerScrollPane)) {
            remove(partitionPie);
            add(centerScrollPane, BorderLayout.CENTER);
            revalidate();
            repaint();
        }
    }

    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(PartitionModel.NODE_TRANSFORMER) || evt.getPropertyName().equals(PartitionModel.EDGE_TRANSFORMER)) {
            refreshTransformerBuilder();
        } else if (evt.getPropertyName().equals(PartitionModel.NODE_PARTITION) || evt.getPropertyName().equals(PartitionModel.EDGE_PARTITION)) {
            refreshTransformerBuilder();
            refreshPartitions();
            refreshGrouped();
            refreshPie();
        } else if (evt.getPropertyName().equals(PartitionModel.WAITING)) {
            refreshWaiting();
        } else if (evt.getPropertyName().equals(PartitionModel.SELECTED_PARTIONING)) {
            refreshTransformerBuilder();
            refreshPartitions();
            refreshGrouped();
            refreshPie();
        } else if (evt.getPropertyName().equals(PartitionModel.PIE)) {
            refreshPie();
        }
    }

    private void refreshModel() {
        refreshEnable();
        if (model != null) {
            refreshPartitions();
            refreshTransformerBuilder();
            refreshWaiting();
            refreshGrouped();
            refreshPie();
        }
    }

    public void setup(PartitionModel model) {
        if (model != null) {
            this.model = model;
            model.addPropertyChangeListener(this);
        }
        refreshModel();
    }

    public void unsetup() {
        if (model != null) {
            model.removePropertyChangeListener(this);
            model = null;
        }
        refreshModel();
    }

    private void setEnable(boolean enable) {
        applyButton.setEnabled(enable);
        partitionComboBox.setEnabled(enable);
        groupLink.setEnabled(enable);
        pieLink.setEnabled(enable);
        refreshButton.setEnabled(enable);
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton applyButton;
    private javax.swing.JScrollPane centerScrollPane;
    private javax.swing.JPanel chooserPanel;
    private javax.swing.JPanel controlPanel;
    private org.jdesktop.swingx.JXHyperlink groupLink;
    private javax.swing.JComboBox partitionComboBox;
    private org.jdesktop.swingx.JXHyperlink pieLink;
    private javax.swing.JLabel refreshBusyLabel;
    private javax.swing.JButton refreshButton;
    private javax.swing.JToolBar refreshToolbar;
    // End of variables declaration//GEN-END:variables
}
